RSpec.shared_examples_for 'Msf::DBManager::ExploitAttempt' do
  it { is_expected.to respond_to :report_exploit }
  it { is_expected.to respond_to :report_exploit_attempt }
  it { is_expected.to respond_to :report_exploit_failure }
  it { is_expected.to respond_to :report_exploit_success }

  describe '#report_exploit_failure' do
    subject(:report_exploit_failure) do
      db_manager.report_exploit_failure(opts)
    end

    let(:run) do
      match
      FactoryGirl.create(:automatic_exploitation_run, user: workspace.owner,workspace:workspace, match_set_id: match_set.id)
    end

    let(:match_set) do
      FactoryGirl.create(:automatic_exploitation_match_set, user: workspace.owner,workspace:workspace)
    end

    let(:match) do
      FactoryGirl.create(:automatic_exploitation_match,
                         match_set_id: match_set.id,
                         matchable_id:vuln_with_match.id,
                         matchable_type: "Mdm::Vuln"
      )
    end

    let(:vuln_with_match) do
      FactoryGirl.create(:mdm_vuln)
    end

    let(:host) do
      FactoryGirl.create(:mdm_host, workspace:workspace)
    end

    let(:workspace) do
      FactoryGirl.create(:mdm_workspace)
    end

    let(:refs) do
      [ FactoryGirl.create(:mdm_ref) ]
    end

    context "with a run" do
      let(:opts) do
        {
          workspace: workspace,
          refs: refs,
          host: host,
          vuln: vuln_with_match,
          run_id: run.id
        }
      end

      context 'with a vuln' do
        specify do
          expect {
            report_exploit_failure
          }.to change(Mdm::VulnAttempt,:count).by(1)
        end

        it "should create a match result" do
          expect {
            report_exploit_failure
          }.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(1)
        end

        it "should create a match result with state FAILED" do
          report_exploit_failure

          expect(
            MetasploitDataModels::AutomaticExploitation::MatchResult.where(
              match_id: match.id,
              state: MetasploitDataModels::AutomaticExploitation::MatchResult::FAILED
            )
          ).to exist
        end

        context "calling report_exploit_success" do
          after(:example) do
            report_exploit_failure
          end

          it "should call create_match_result_for_vuln" do
            expect(db_manager).to receive(:create_match_result_for_vuln)
          end

          it "should call create_match_result" do
            expect(db_manager).to receive(:create_match_result)
          end

          it "should not call create_match_for_vuln" do
            expect(db_manager).not_to receive(:create_match_for_vuln)
          end
        end

      end

      context 'without a vuln' do
        let(:vuln_with_match) { nil }

        let(:match) {nil}

        specify do
          expect {
            report_exploit_failure
          }.not_to change(Mdm::VulnAttempt, :count)
        end

        it "should not create a match result" do
          expect {
            report_exploit_failure
          }.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(0)
        end

        context "calling report_exploit_success" do
          after(:example) do
            report_exploit_failure
          end

          it "should not call create_match_result_for_vuln" do
            expect(db_manager).not_to receive(:create_match_result_for_vuln)
          end

          it "should not call create_match_result" do
            expect(db_manager).not_to receive(:create_match_result)
          end

          it "should not call create_match_for_vuln" do
            expect(db_manager).not_to receive(:create_match_for_vuln)
          end
        end

      end
    end

    context "without a run" do
      let(:vuln) do
        FactoryGirl.create(:mdm_vuln)
      end

      let(:opts) do
        {
          workspace: workspace,
          refs: refs,
          host: host,
          vuln: vuln,
        }
      end

      context 'with a vuln' do
        specify do
          expect {
            report_exploit_failure
          }.to change(Mdm::VulnAttempt,:count).by(1)
        end

        it "should not create a match result" do
          expect {
            report_exploit_failure
          }.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(0)
        end

        context "calling report_exploit_success" do
          after(:example) do
            report_exploit_failure
          end

          it "should call create_match_result_for_vuln" do
            expect(db_manager).to receive(:create_match_result_for_vuln)
          end

          it "should not call create_match_result" do
            expect(db_manager).not_to receive(:create_match_result)
          end

          it "should not call create_match_for_vuln" do
            expect(db_manager).not_to receive(:create_match_for_vuln)
          end
        end

      end

      context 'without a vuln' do
        let(:vuln) { nil }

        specify do
          expect {
            report_exploit_failure
          }.not_to change(Mdm::VulnAttempt, :count)
        end

        it "should not create a match result" do
          expect {
            report_exploit_failure
          }.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(0)
        end

        context "calling report_exploit_success" do
          after(:example) do
            report_exploit_failure
          end

          it "should not call create_match_result_for_vuln" do
            expect(db_manager).not_to receive(:create_match_result_for_vuln)
          end

          it "should not call create_match_result" do
            expect(db_manager).not_to receive(:create_match_result)
          end

          it "should not call create_run_for_vuln" do
            expect(db_manager).not_to receive(:create_match_for_vuln)
          end
        end

      end
    end
  end


  describe '#report_exploit_success' do
    subject(:report_exploit_success) do
      db_manager.report_exploit_success(opts)
    end

    let(:session_id) do
      FactoryGirl.create(:session, host: host).id
    end

    let(:run) do
      match
      FactoryGirl.create(:automatic_exploitation_run, user: workspace.owner,workspace:workspace, match_set_id: match_set.id)
    end

    let(:match_set) do
      FactoryGirl.create(:automatic_exploitation_match_set, user: workspace.owner,workspace:workspace)
    end

    let(:match) do
      FactoryGirl.create(:automatic_exploitation_match,
                         match_set_id: match_set.id,
                         matchable_id:vuln_with_match.id,
                         matchable_type: "Mdm::Vuln"
      )
    end

    let(:vuln_with_match) do
      FactoryGirl.create(:mdm_vuln)
    end

    let(:host) do
      FactoryGirl.create(:mdm_host, workspace:workspace)
    end

    let(:workspace) do
      FactoryGirl.create(:mdm_workspace)
    end

    let(:refs) do
      [ FactoryGirl.create(:mdm_ref) ]
    end

    context "with a run" do
      let(:opts) do
        {
          workspace: workspace,
          refs: refs,
          host: host,
          vuln: vuln_with_match,
          run_id: run.id,
          session_id: session_id
        }
      end

      context 'with a vuln' do
        specify do
          expect {
            report_exploit_success
          }.to change(Mdm::VulnAttempt,:count).by(1)
        end

        it "should create a match result" do
          expect {
            report_exploit_success
          }.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(1)
        end

        it "should create a match result with state SUCCEEDED" do
          report_exploit_success
          expect(
            MetasploitDataModels::AutomaticExploitation::MatchResult.where(
              match_id: match.id,
              state: MetasploitDataModels::AutomaticExploitation::MatchResult::SUCCEEDED
            )
          ).to exist
        end

        context "calling report_exploit_success" do
          after(:example) do
            report_exploit_success
          end

          it "should call create_match_result_for_vuln" do
            expect(db_manager).to receive(:create_match_result_for_vuln)
          end

          it "should call create_match_result" do
            expect(db_manager).to receive(:create_match_result)
          end

          it "should not call create_match_for_vuln" do
            expect(db_manager).not_to receive(:create_match_for_vuln)
          end
        end

      end

      context 'without a run' do
        let(:vuln_with_match) { nil }

        let(:match) {nil}

        specify do
          expect {
            report_exploit_success
          }.not_to change(Mdm::VulnAttempt, :count)
        end

        it "should not create a match result" do
          expect {
            report_exploit_success
          }.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(0)
        end

        context "calling report_exploit_success" do
          after(:example) do
            report_exploit_success
          end

          it "should not call create_match_result_for_vuln" do
            expect(db_manager).not_to receive(:create_match_result_for_vuln)
          end

          it "should not call create_match_result" do
            expect(db_manager).not_to receive(:create_match_result)
          end

          it "should not call create_match_for_vuln" do
            expect(db_manager).not_to receive(:create_match_for_vuln)
          end
        end

      end
    end

    context "without a run" do
      let(:vuln) do
        FactoryGirl.create(:mdm_vuln)
      end

      let(:opts) do
        {
          workspace: workspace,
          refs: refs,
          host: host,
          vuln: vuln,
        }
      end

      context 'with a vuln' do
        specify do
          expect {
            report_exploit_success
          }.to change(Mdm::VulnAttempt,:count).by(1)
        end

        it "should not create a match result" do
          expect {
            report_exploit_success
          }.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(0)
        end

        context "calling report_exploit_success" do
          after(:example) do
            report_exploit_success
          end

          it "should call create_match_result_for_vuln" do
            expect(db_manager).to receive(:create_match_result_for_vuln)
          end

          it "should not call create_match_result" do
            expect(db_manager).not_to receive(:create_match_result)
          end

          it "should not call create_match_for_vuln" do
            expect(db_manager).not_to receive(:create_match_for_vuln)
          end
        end

      end

      context 'without a vuln' do
        let(:vuln) { nil }

        specify do
          expect {
            report_exploit_success
          }.not_to change(Mdm::VulnAttempt, :count)
        end

        it "should not create a match result" do
          expect {
            report_exploit_success
          }.to change(MetasploitDataModels::AutomaticExploitation::MatchResult,:count).by(0)
        end

        context "calling report_exploit_success" do
          after(:example) do
            report_exploit_success
          end

          it "should not call create_match_result_for_vuln" do
            expect(db_manager).not_to receive(:create_match_result_for_vuln)
          end

          it "should not call create_match_result" do
            expect(db_manager).not_to receive(:create_match_result)
          end

          it "should not call create_match_for_vuln" do
            expect(db_manager).not_to receive(:create_match_for_vuln)
          end
        end

      end
    end

  end
end
